﻿using System;
using System.Reflection;
using MessagingWPF.Infrastructure.Utilities;
using Microsoft.Practices.ServiceLocation;

namespace MessagingWPF.Infrastructure
{
	/// <summary>
	/// A subscription that specifies the type of the subscriber.
	/// </summary>
	[Serializable]
	public class TypeSubscription : Subscription
	{
		[NonSerialized] private readonly IServiceLocator _Container;

		private readonly MethodInfo _HandleMethod;

		/// <summary>
		/// Initializes a new instance of the class.
		/// </summary>
		/// <param name="messageType">Type of the message.</param>
		/// <param name="subscriberType">Type of the subscriber.</param>
		/// <param name="container">The container.</param>
		public TypeSubscription(Type messageType, Type subscriberType, IServiceLocator container)
			: base(messageType)
		{
			Ensure.ArgumentIsNotNull(subscriberType, "subscriberType");
			Ensure.ArgumentIsNotNull(container, "container");

			SubscriberType = subscriberType;
			_Container = container;
			_HandleMethod = GetHandleMethod(subscriberType);
		}

		/// <summary>
		/// Gets the type of the subscriber.
		/// </summary>
		/// <value>The type of the subscriber.</value>
		public Type SubscriberType { get; private set; }

		/// <summary>
		/// Serves as a hash function for a particular type.
		/// </summary>
		/// <returns>A hash code for the current object.</returns>
		public override int GetHashCode()
		{
			var hash = GetType().GetHashCode();
			hash ^= MessageType.GetHashCode();
			hash ^= SubscriberType.GetHashCode();
			return hash;
		}

		/// <summary>
		/// Indicates whether the subscription is equal to the specified subscription.
		/// </summary>
		/// <param name="subscription">A subscription to compare with this subscription.</param>
		/// <returns>
		/// <c>True</c> if the current object is equal to the <paramref name="other"/> parameter;
		/// otherwise, <c>false</c>.
		/// </returns>
		protected override bool EqualsCore(Subscription subscription)
		{
			var other = subscription as TypeSubscription;

			return other != null &&
				other.MessageType == MessageType &&
					other.SubscriberType == SubscriberType;
		}

		/// <summary>
		/// Core implementation of sending the message.
		/// </summary>
		/// <param name="message">The message.</param>
		protected override void SendMessageCore(object message)
		{
			var instance = _Container.GetInstance(SubscriberType);
			_HandleMethod.Invoke(instance, new[] {message});
		}
	}
}